﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace CellarDoor.Gui.Elements
{
    public class UiSlideBar : UiElement
    {
        #region Properties and fields
        private UiOrientation barStyle = UiOrientation.Vertical;
        /// <summary>
        /// Gets and sets the bar style. 
        /// </summary>
        public UiOrientation BarStyle
        {
            get { return barStyle; }
            set 
            {
                if (barStyle != value)
                {
                    barStyle = value;
                    OnBarStyleChanged();
                }
            }
        }

        private float minValue = 0;
        /// <summary>
        /// Gets and sets the minimum value to slide to
        /// </summary>
        public float MinValue
        {
            get { return minValue; }
            set { minValue = value; }
        }

        private float maxValue = 1;
        /// <summary>
        /// Gets and sets the maximum value to slide to
        /// </summary>
        public float MaxValue
        {
            get { return maxValue; }
            set { maxValue = value; }
        }

        private float value;
        /// <summary>
        /// Gets and seth the value of the slidebar. This is clamped to be in range 
        /// between <see cref="MinValue"/> and <see cref="MaxValue"/>
        /// </summary>
        public float Value
        {
            get { return value; }
            set
            {
                float oldvalue = this.value;
                this.value = MathHelper.Clamp(value, minValue, maxValue);
                OnValueChanged(oldvalue);
            }
        }

        private string format = "0.00";
        /// <summary>
        /// Gets and sets the format string to format the text value of this slider
        /// </summary>
        public string Format
        {
            get { return format; }
            set { format = value; }
        }

        /// <summary>
        /// Gets the noramlized value between 0 and 1
        /// </summary>
        public float NormalizedValue
        {
            get { return (value - minValue) / (maxValue - minValue); }
            set { Value = value * (maxValue - minValue) + minValue;  }
        }

        private UiButton button;
        /// <summary>
        /// Gets the slide button.
        /// </summary>
        public UiButton Button
        {
            get { return button; }
        }
        #endregion

        #region Events
        /// <summary>
        /// Occurs when value has changed.
        /// </summary>
        public event EventHandler ValueChanged;

        /// <summary>
        /// Occurs when bar styl has changed.
        /// </summary>
        public event EventHandler BarStyleChanged;
        #endregion

        #region Constructor
        /// <summary>
        /// Creates a slide bar element
        /// </summary>
        /// <param name="manager"></param>
        public UiSlideBar()
            : base()
        {
            Initialize();
        }

        /// <summary>
        /// Creates a slide bar element
        /// </summary>
        /// <param name="parent"></param>
        public UiSlideBar(UiElement parent)
            : base(parent)
        {
            Initialize();
        }

        private void Initialize()
        {
            Text = value.ToString();

            button = new UiButton(this);
            button.Dragable = true;
            button.DragFromCenter = true;
            button.DragLimitEnable = true;
            button.MouseDrag += buttonMouseDrag;
            button.Size = new Vector2(25);
            this.BarStyle = Gui.UiOrientation.Horizontal;
        }
        #endregion

        protected virtual void OnBarStyleChanged()
        {
            if (barStyle == UiOrientation.Horizontal)
            {
                this.ElementName = "SlideBarH";
                this.TextAlign = UiAlignStyle.Middle | UiAlignStyle.OutsideRight;

                button.ElementName = "SlideBallH";
            }
            else
            {
                this.ElementName = "SlideBarV";
                this.TextAlign = UiAlignStyle.Middle | UiAlignStyle.OutsideTop;

                button.ElementName = "SlideBallV";
            }

            if (BarStyleChanged != null)
            {
                BarStyleChanged(this, EventArgs.Empty);
            }
        }

        protected virtual void OnValueChanged(float oldValue)
        {
            ButtonPositionFromValue();
            Text = value.ToString(format);
            if (ValueChanged != null && oldValue != value)
            {
                ValueChanged(this, EventArgs.Empty);
            }
        }

        protected override void OnMousePress(UiMouseProxy mouse)
        {
            Vector2 point = new Vector2(mouse.PositionNew.X, mouse.PositionNew.Y) / Manager.ElementScale;
            point -= button.Size / 2.0f;
            ValueFromPoint(point);
            base.OnMousePress(mouse);
        }

        void buttonMouseDrag(object sender, EventArgs e)
        {
            UiMouseArgs mouse = e as UiMouseArgs;
            if (mouse != null)
            {
                Vector2 point = new Vector2(mouse.Mouse.PositionNew.X, mouse.Mouse.PositionNew.Y) / Manager.ElementScale;
                point -= button.Size / 2.0f;
                ValueFromPoint(point);
            }
        }

        private void ButtonPositionFromValue()
        {
            Vector2 offset = Vector2.Zero;
            if (barStyle == UiOrientation.Vertical)
            {
                float usedSize = this.Size.Y - button.Size.Y;
                offset.X = 0;
                offset.Y = usedSize * NormalizedValue;
            }
            else
            {
                float usedSize = this.Size.X - button.Size.X;
                offset.X = usedSize * NormalizedValue;
                offset.Y = 0;
            }
            button.Location = offset;
        }

        protected void ValueFromPoint(Vector2 point)
        {
            if (barStyle == UiOrientation.Vertical)
            {
                float usedSize = this.Size.Y - button.Size.Y;
                SetPercentValue((point.Y - this.Layout.Position.Y) / usedSize);
            }
            else
            {
                float usedSize = this.Size.X - button.Size.X;
                SetPercentValue((point.X - this.Layout.Position.X) / usedSize);
            }
        }

        private void SetPercentValue(float percent)
        {
            percent = MathHelper.Clamp(percent, 0, 1);
            Value = MathHelper.Lerp(minValue, maxValue, percent);
        }

        public override void Clear()
        {
            base.Clear();
            AddElement(Button);
        }
    }
}
